/**
* @program: src
*
* @description:服务注册发现
*
* @author: Mr.chen
*
* @create: 2022-04-01 13:57
**/
package etcdregister

import (
	"fmt"
	"github.com/coreos/etcd/clientv3"
	"errors"
	uuid "github.com/satori/go.uuid"
	"strings"
	"sync"
	"time"
)
var once sync.Once
type EtcdService struct {
	r  map[string]*Register // 当前服务的节点名称
	rMapLock sync.RWMutex       // 读写锁
	n *NodesManager
	d *Discovery

}

func NewEtcdService() *EtcdService  {

	return &EtcdService{
		r:make(map[string]*Register),
		n:NewNodeManager(),

	}
}
// 注册etcd服务
func (e *EtcdService) Register(serviceName,addr string,Endpoints []string) (err error) {
	if _,ok := e.r[serviceName]; ok {
		return errors.New("The current service is already registered, please rename")
	}
	reg, err := NewRegister(&NodeInfo{
		Name: serviceName,
		Addr: addr,
		UniqueId: fmt.Sprintf("discovery/%s/instance_id/%s",serviceName,GenUUID()),
	}, clientv3.Config {
		Endpoints:   Endpoints,
		DialTimeout: 5 * time.Second,
	})
	if err != nil {
		return err
	}
	e.rMapLock.RLock()
	e.r[serviceName] = reg
	e.rMapLock.RUnlock()
	go e.r[serviceName].Run()
	return nil
}
// 关闭etcd服务
func (e *EtcdService) Close(serviceName string)  {
	e.r[serviceName].Stop()
}
// 服务对应的的节点
func (e *EtcdService) Nodes(serviceName string) (addrs []string) {
	addrs = make([]string,0)
	if _,ok :=e.r[serviceName];!ok {
		return addrs
	} else {
		return e.n.GetNodes(serviceName)
	}
}
/**
 * 负载均衡算法
 * @access  public
 * @param   string     name  节点的名称
 * @param   string     balanceName  名称，roundrobin(轮询)，random(随机)
 * @return  string addr 地址
 * @return  error err
 */
func (e *EtcdService) GetSingleNodeAddr(serviceName,balanceName string) (addr string,err error) {

	if _,ok :=e.r[serviceName];!ok {
		return "",errors.New("Service does not exist, please try again")
	} else {
		return e.n.GetBalancerNodes(serviceName,balanceName)
	}
}
// 服务发现，一个项目只启动一次
func (e *EtcdService) Discovery(name,addr string,Endpoints []string)  {
	once.Do(func() {
		dis, _ := NewDiscovery(&NodeInfo{
			Name: GenUUID(),
			Addr: addr,
		}, clientv3.Config{
			Endpoints:   Endpoints,
			DialTimeout: 5 * time.Second,
		}, e.n)
		//启动服务发现，将已经存在的node加入节点管理中
		dis.pull()
		// 监听服务情况发现
		go dis.watch()
		e.d = dis
	})

}
func GenUUID() string {
	uuidFunc := uuid.NewV4()
	uuidStr := uuidFunc.String()
	uuidStr = strings.Replace(uuidStr, "-", "", -1)
	uuidByt := []rune(uuidStr)
	return string(uuidByt[8:24])
}

// 关闭etcd发现服务
func (e *EtcdService) CloseDiscover()  {
	e.d.Close()
}